package com.pine.common.database.batch;

import com.baomidou.mybatisplus.core.batch.BatchSqlSession;
import com.baomidou.mybatisplus.core.batch.MybatisBatch;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import jakarta.annotation.Resource;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Function;

/**
 * mybatis plus 批量插入组件<br>
 * 参考：<a href="https://baomidou.com/guides/batch-operation/">https://baomidou.com/guides/batch-operation/</a>
 * <p>
 * 例如：
 * <pre>
 *     MybatisPlusBatchOperationComponent component = SpringBeanUtil.getByClass(MybatisPlusBatchOperationComponent.class);
 *     SysUserMapper sysUserMapper = SpringBeanUtil.getByClass(SysUserMapper.class);
 *
 *         List<SysUser> sysUsers = new ArrayList<>();
 *         for (int i = 0; i < 5; i++) {
 *             SysUser sysUser = new SysUser();
 *             sysUser.setId((long) (i + 3));
 *             sysUser.setUsername("username" + i);
 *             sysUser.setPassword("password" + i);
 *             sysUser.setLoginAccount("loginAccount" + i+10);
 *             sysUser.setEmail("email" + i);
 *             sysUser.setPhone("phone" + i);
 *             sysUser.setSex(SexEnum.MALE);
 *             sysUser.setStatus(StatusEnum.DISABLE);
 *             sysUser.setAge(i+2);
 *             sysUsers.add(sysUser);
 *         }
 *
 *         MybatisBatch.Method<SysUser> batchMethod = new MybatisBatch.Method<>(SysUserMapper.class);
 *         // 定义插入条件。
 *         BiPredicate<BatchSqlSession, SysUser> insertPredicate = ((sqlSession, entity) -> {
 *             // 根据条件查询实体是否存在。
 *             LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
 *             queryWrapper.eq(SysUser::getId, entity.getId());
 *             queryWrapper.or().eq(SysUser::getLoginAccount, entity.getLoginAccount());
 *             List<SysUser> selectedList = sysUserMapper.selectList(queryWrapper);
 *             // 如果查询结果为空，则表示实体不存在，可以插入。
 *             return ObjectUtils.isEmpty(selectedList);
 *         });
 *
 *         component.saveOrUpdateByCustomPredicate(sysUsers, batchMethod, insertPredicate);
 * </pre>
 *
 * @author pine manage
 * @since 2024-08-09
 */
//@Component
@Deprecated
public class MybatisPlusBatchOperationComponent {

    @Resource
    private SqlSessionFactory sqlSessionFactory;

    @Transactional(rollbackFor = Exception.class)
    public <T> List<BatchResult> save(List<T> dataList, Class<? extends BaseMapper<T>> mapperClass) {
        MybatisBatch.Method<T> batchMethod = new MybatisBatch.Method<>(mapperClass);
        return MybatisBatchUtils.execute(sqlSessionFactory, dataList, batchMethod.insert());
    }

    /**
     * 保存或更新实体列表。
     *
     * @param dataList               待保存或更新的实体列表。
     * @param mapperClass            实体对应的Mapper类，用于生成SQL语句。
     * @param primaryKeyTypeFunction 用于从实体中提取主键值的函数。
     * @param <T>                    实体类型。
     * @param <PrimaryKeyType>       主键类型，必须继承自Serializable。
     * @return 返回保存或更新的结果列表。
     */
    @Transactional(rollbackFor = Exception.class)
    public <T, PrimaryKeyType extends Serializable> List<BatchResult> saveOrUpdate(List<T> dataList, Class<? extends BaseMapper<T>> mapperClass, Function<T, PrimaryKeyType> primaryKeyTypeFunction) {
        // 创建MybatisBatch.Method实例，用于后续批量操作。
        MybatisBatch.Method<T> batchMethod = new MybatisBatch.Method<>(mapperClass);

        // 定义插入条件，通过应用primaryKeyTypeFunction来检查实体是否已存在。
        BiPredicate<BatchSqlSession, T> insertPredicate = ((sqlSession, entity) -> {
            // 根据主键值查询实体是否存在。
            List<T> selectById = sqlSession.selectList(batchMethod.get(SqlMethod.SELECT_BY_ID.getMethod()).getStatementId(), primaryKeyTypeFunction.apply(entity));
            // 如果查询结果为空，则表示实体不存在，可以插入。
            return ObjectUtils.isEmpty(selectById);
        });

        return saveOrUpdateByCustomPredicate(dataList, batchMethod, insertPredicate);
    }

    @Transactional(rollbackFor = Exception.class)
    public <T> List<BatchResult> saveOrUpdateByCustomPredicate(List<T> dataList, MybatisBatch.Method<T> batchMethod, BiPredicate<BatchSqlSession, T> insertPredicate) {
        // 调用MybatisBatchUtils的saveOrUpdate方法，执行批量保存或更新操作。
        return MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, dataList, batchMethod.insert(), insertPredicate, batchMethod.updateById());
    }

}
